﻿using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Web;
using Sp.SuperWebHost.NetFramework;

namespace Sp.SuperWebHost
{
    class NewConnection :MarshalByRefObject
    {
        private readonly ServerSocket _serverSocket;
        private readonly AsyncUserToken _asyncUserToken;
        internal NewConnection(ServerSocket socket, AsyncUserToken asyncUserToken)
        {
            _serverSocket = socket;
            _asyncUserToken = asyncUserToken;
        }

        internal string RemoteIp
        {

            get { return _asyncUserToken.EndPoint.Address.ToString(); }
        }

        internal bool Connected
        {

            get { return _serverSocket.IsOnline(_asyncUserToken.ConnectionId); }
        }

        internal bool ProduceIfError()
        {
           
            if (_asyncUserToken.DataBuffer.Count==0)
            {
                WriteErrorAndClose(400);
            }
            return false;
        }

        internal void CloseConnect()
        {
            _serverSocket.Disconnect(_asyncUserToken.ConnectionId);
        }

        internal void WriteErrorAndClose(int statusCode)
        {
            this.WriteErrorAndClose(statusCode, null);
        }

        internal void WriteErrorAndClose(int statusCode, string message)
        {
            this.WriteEntireResponseFromString(statusCode, "Content-type:text/html;charset=utf-8\r\n", this.GetErrorResponseBody(statusCode, message), false);
        }

        /// <summary>
        /// 状态码为100
        /// </summary>
        internal void Write100Continue()
        {
            this.WriteEntireResponseFromString(100, null, null, true);
        }
        /// <summary>
        /// Writes the entire response from file.
        /// </summary>
        /// <param name="filename">The filename.</param>
        /// <param name="keepAlive">true to keep alive; otherwise, false.</param>
        [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Reviewed.")]
        internal void WriteEntireResponseFromFile(string filename, bool keepAlive)
        {
            if (!File.Exists(filename))
            {
                this.WriteErrorAndClose(404);
                return;
            }

            string text = NewConnection.MakeContentTypeHeader(filename);

            if (text == null)
            {
                this.WriteErrorAndClose(403);
                return;
            }

            bool flag = false;

            FileStream fileStream = null;

            try
            {
                fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read);

                int num = (int)fileStream.Length;

                byte[] buffer = new byte[num];

                int num2 = fileStream.Read(buffer, 0, num);

                _serverSocket.Send(_asyncUserToken.ConnectionId, Encoding.UTF8.GetBytes(NewConnection.MakeResponseHeaders(200, text, num2, keepAlive)));

                _serverSocket.Send(_asyncUserToken.ConnectionId,buffer.Take(num2).ToArray());

                flag = true;
            }
            catch (SocketException)
            {
            }
            finally
            {
                if (!keepAlive || !flag)
                {
                    this.CloseConnect();
                }

                if (fileStream != null)
                {
                    fileStream.Dispose();
                }
            }
        }

        internal void WriteEntireResponseFromString(int statusCode, string extraHeaders, string body, bool keepAlive)
        {
            try
            {
                int contentLength = (body != null) ? Encoding.UTF8.GetByteCount(body) : 0;
                var content =
                    Encoding.UTF8.GetBytes(
                        NewConnection.MakeResponseHeaders(statusCode, extraHeaders, contentLength, keepAlive) + body);
                _serverSocket.Send(_asyncUserToken.ConnectionId,content);
                //this._serverSocket.Send(Encoding.UTF8.GetBytes(Connection.MakeResponseHeaders(statusCode, extraHeaders, contentLength, keepAlive) + body));
            }
            catch (SocketException)
            {
            }
            finally
            {
                if (!keepAlive)
                {
                    this.CloseConnect();
                }
            }
        }


        internal byte[] ReadRequestBytes(int maxLen)
        {
            return _asyncUserToken.DataBuffer.Take(maxLen).ToArray();
        }


        private string GetErrorResponseBody(int statusCode, string message)
        {
            var text = Messages.FormatErrorMessageBody(statusCode, "/");

            if (!string.IsNullOrWhiteSpace(message))
            {
                text = text + "\r\n<!--\r\n" + message + "\r\n-->";
            }

            return text;
        }

        

        private static string MakeResponseHeaders(int statusCode, string moreHeaders, int contentLength, bool keepAlive)
        {
            /*
            HTTP/1.1 200 OK
            Server: openresty/1.7.2.1
            Date: Thu, 05 Feb 2015 03:01:50 GMT
            Content-Type: text/html; charset=utf-8
            Transfer-Encoding: chunked
            Connection: keep-alive
            Vary: Accept-Encoding
            X-Powered-By: PHP/5.4.26
            Content-Encoding: gzip

             */
            var stringBuilder = new StringBuilder();
            stringBuilder.AppendFormat("HTTP/1.1 {0} {1}\r\n", statusCode, HttpWorkerRequest.GetStatusDescription(statusCode));
            stringBuilder.AppendFormat("Server:{0}/{1}\r\n", Constants.VWDName, Constants.VersionString);
            stringBuilder.AppendFormat("Date:{0}\r\n", DateTime.Now.ToUniversalTime().ToString("R", DateTimeFormatInfo.InvariantInfo));
            stringBuilder.AppendFormat("X-Powered-By:{0}/{1}\r\n",Constants.APPServer,Constants.VersionString);
            if (contentLength >= 0)
            {
                stringBuilder.AppendFormat("Content-Length: {0}\r\n", contentLength);
            }

            if (moreHeaders != null)
            {
                stringBuilder.Append(moreHeaders);
            }
            if (!keepAlive)
            {
                stringBuilder.Append("Connection: Close\r\n");
            }
            stringBuilder.Append("\r\n");

            return stringBuilder.ToString();
        }

        internal void WriteHeaders(int statusCode, string extraHeaders)
        {
            string text = NewConnection.MakeResponseHeaders(statusCode, extraHeaders, -1, false);

            try
            {
               _serverSocket.Send(_asyncUserToken.ConnectionId,Encoding.UTF8.GetBytes(text));
            }
            catch (SocketException)
            {
            }
        }

        internal void WriteBody(byte[] data, int offset, int length)
        {
            try
            {
                _serverSocket.Send(_asyncUserToken.ConnectionId, data.Skip(offset).Take(length).ToArray());
            }
            catch (SocketException)
            {
            }
        }

        /// <summary>
        /// 构建Response的Header
        /// </summary>
        /// <param name="filename">The filename.</param>
        /// <returns>Content type header string.</returns>
        private static string MakeContentTypeHeader(string filename)
        {
            string text = null;
            var key = new FileInfo(filename).Extension.ToLowerInvariant();
            switch (key)
            {
                case ".bmp":
                    text = "image/bmp";
                    break;

                case ".css":
                    text = "text/css";
                    break;

                case ".gif":
                    text = "image/gif";
                    break;
                case ".ico":
                    text = "image/x-icon";
                    break;
                case ".htm":
                case ".html":
                    text = "text/html";
                    break;
                case ".jpe":
                case ".jpeg":
                case ".jpg":
                    text = "image/jpeg";
                    break;
                case ".js":
                    text = "application/x-javascript";
                    break;
            }

            if (text == null)
            {
                return null;
            }
            return "Content-Type: " + text + "\r\n";
        }
      
    }
}
